package com.google.code.shardbatis.converter;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import net.sf.jsqlparser.expression.AllComparisonExpression;
import net.sf.jsqlparser.expression.AnalyticExpression;
import net.sf.jsqlparser.expression.AnyComparisonExpression;
import net.sf.jsqlparser.expression.BinaryExpression;
import net.sf.jsqlparser.expression.CaseExpression;
import net.sf.jsqlparser.expression.CastExpression;
import net.sf.jsqlparser.expression.DateValue;
import net.sf.jsqlparser.expression.DoubleValue;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.ExpressionVisitor;
import net.sf.jsqlparser.expression.ExtractExpression;
import net.sf.jsqlparser.expression.Function;
import net.sf.jsqlparser.expression.HexValue;
import net.sf.jsqlparser.expression.IntervalExpression;
import net.sf.jsqlparser.expression.JdbcNamedParameter;
import net.sf.jsqlparser.expression.JdbcParameter;
import net.sf.jsqlparser.expression.JsonExpression;
import net.sf.jsqlparser.expression.KeepExpression;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.expression.MySQLGroupConcat;
import net.sf.jsqlparser.expression.NullValue;
import net.sf.jsqlparser.expression.NumericBind;
import net.sf.jsqlparser.expression.OracleHierarchicalExpression;
import net.sf.jsqlparser.expression.Parenthesis;
import net.sf.jsqlparser.expression.RowConstructor;
import net.sf.jsqlparser.expression.SignedExpression;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.TimeValue;
import net.sf.jsqlparser.expression.TimestampValue;
import net.sf.jsqlparser.expression.UserVariable;
import net.sf.jsqlparser.expression.WhenClause;
import net.sf.jsqlparser.expression.WithinGroupExpression;
import net.sf.jsqlparser.expression.operators.arithmetic.Addition;
import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseAnd;
import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseOr;
import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseXor;
import net.sf.jsqlparser.expression.operators.arithmetic.Concat;
import net.sf.jsqlparser.expression.operators.arithmetic.Division;
import net.sf.jsqlparser.expression.operators.arithmetic.Modulo;
import net.sf.jsqlparser.expression.operators.arithmetic.Multiplication;
import net.sf.jsqlparser.expression.operators.arithmetic.Subtraction;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
import net.sf.jsqlparser.expression.operators.relational.Between;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.expression.operators.relational.ExistsExpression;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.GreaterThan;
import net.sf.jsqlparser.expression.operators.relational.GreaterThanEquals;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.expression.operators.relational.IsNullExpression;
import net.sf.jsqlparser.expression.operators.relational.ItemsListVisitor;
import net.sf.jsqlparser.expression.operators.relational.LikeExpression;
import net.sf.jsqlparser.expression.operators.relational.Matches;
import net.sf.jsqlparser.expression.operators.relational.MinorThan;
import net.sf.jsqlparser.expression.operators.relational.MinorThanEquals;
import net.sf.jsqlparser.expression.operators.relational.MultiExpressionList;
import net.sf.jsqlparser.expression.operators.relational.NotEqualsTo;
import net.sf.jsqlparser.expression.operators.relational.RegExpMatchOperator;
import net.sf.jsqlparser.expression.operators.relational.RegExpMySQLOperator;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.select.FromItemVisitor;
import net.sf.jsqlparser.statement.select.Join;
import net.sf.jsqlparser.statement.select.LateralSubSelect;
import net.sf.jsqlparser.statement.select.OrderByElement;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.SelectVisitor;
import net.sf.jsqlparser.statement.select.SetOperationList;
import net.sf.jsqlparser.statement.select.SubJoin;
import net.sf.jsqlparser.statement.select.SubSelect;
import net.sf.jsqlparser.statement.select.ValuesList;
import net.sf.jsqlparser.statement.select.WithItem;

public class SelectSqlConverterModifier implements SelectVisitor, FromItemVisitor, ExpressionVisitor, ItemsListVisitor {

	private String tn = null;
	private String originalTable = null;
	private List<SqlConverterOrderBy> orderBys = null;

	public SelectSqlConverterModifier() {
		this(null);
	}
	public SelectSqlConverterModifier(String tn) {
		this.tn = tn;
		this.orderBys = new ArrayList<SqlConverterOrderBy>();
	}
	
	public String getOriginalTable() {
		return originalTable;
	}
	public List<SqlConverterOrderBy> getOrderBys() {
		return orderBys;
	}
	
	@Override
	public void visit(ExpressionList paramExpressionList) {
		Iterator<Expression> iter = paramExpressionList.getExpressions().iterator();
		while (iter.hasNext()) {
			// Expression expression = (Expression) iter.next();
			Expression expression = iter.next();
			expression.accept(this);
		}
	}

	@Override
	public void visit(MultiExpressionList paramMultiExpressionList) {
	}

	@Override
	public void visit(NullValue paramNullValue) {
	}

	@Override
	public void visit(Function paramFunction) {
	}

	@Override
	public void visit(SignedExpression paramSignedExpression) {
	}

	@Override
	public void visit(JdbcParameter paramJdbcParameter) {
	}

	@Override
	public void visit(JdbcNamedParameter paramJdbcNamedParameter) {
	}

	@Override
	public void visit(DoubleValue paramDoubleValue) {
	}

	@Override
	public void visit(LongValue paramLongValue) {
	}

	@Override
	public void visit(HexValue paramHexValue) {
	}

	@Override
	public void visit(DateValue paramDateValue) {
	}

	@Override
	public void visit(TimeValue paramTimeValue) {
	}

	@Override
	public void visit(TimestampValue paramTimestampValue) {
	}

	@Override
	public void visit(Parenthesis paramParenthesis) {
		paramParenthesis.getExpression().accept(this);
	}

	@Override
	public void visit(StringValue paramStringValue) {
	}

	@Override
	public void visit(Addition paramAddition) {
		visitBinaryExpression(paramAddition);
	}

	@Override
	public void visit(Division paramDivision) {
		visitBinaryExpression(paramDivision);
	}

	@Override
	public void visit(Multiplication paramMultiplication) {
	}

	@Override
	public void visit(Subtraction paramSubtraction) {
		visitBinaryExpression(paramSubtraction);
	}

	@Override
	public void visit(AndExpression paramAndExpression) {
		visitBinaryExpression(paramAndExpression);
	}

	@Override
	public void visit(OrExpression paramOrExpression) {
		visitBinaryExpression(paramOrExpression);
	}

	@Override
	public void visit(Between between) {
		between.getLeftExpression().accept(this);
		between.getBetweenExpressionStart().accept(this);
		between.getBetweenExpressionEnd().accept(this);
	}

	@Override
	public void visit(EqualsTo paramEqualsTo) {
		visitBinaryExpression(paramEqualsTo);
	}

	@Override
	public void visit(GreaterThan paramGreaterThan) {
		visitBinaryExpression(paramGreaterThan);

	}

	@Override
	public void visit(GreaterThanEquals paramGreaterThanEquals) {
		visitBinaryExpression(paramGreaterThanEquals);
	}

	@Override
	public void visit(InExpression paramInExpression) {
		paramInExpression.getLeftExpression().accept(this);

		paramInExpression.getLeftItemsList().accept(this);
		paramInExpression.getRightItemsList().accept(this);
	}

	@Override
	public void visit(IsNullExpression paramIsNullExpression) {
	}

	@Override
	public void visit(LikeExpression paramLikeExpression) {
		visitBinaryExpression(paramLikeExpression);
	}

	@Override
	public void visit(MinorThan paramMinorThan) {
		visitBinaryExpression(paramMinorThan);
	}

	@Override
	public void visit(MinorThanEquals paramMinorThanEquals) {
		visitBinaryExpression(paramMinorThanEquals);
	}

	@Override
	public void visit(NotEqualsTo paramNotEqualsTo) {
		visitBinaryExpression(paramNotEqualsTo);
	}

	@Override
	public void visit(Column paramColumn) {
	}

	@Override
	public void visit(CaseExpression paramCaseExpression) {
	}

	@Override
	public void visit(WhenClause paramWhenClause) {
	}

	@Override
	public void visit(ExistsExpression paramExistsExpression) {
		paramExistsExpression.getRightExpression().accept(this);
	}

	@Override
	public void visit(AllComparisonExpression paramAllComparisonExpression) {
		paramAllComparisonExpression.getSubSelect().getSelectBody().accept(this);
	}

	@Override
	public void visit(AnyComparisonExpression paramAnyComparisonExpression) {
		paramAnyComparisonExpression.getSubSelect().getSelectBody().accept(this);
	}

	@Override
	public void visit(Concat paramConcat) {
		visitBinaryExpression(paramConcat);
	}

	@Override
	public void visit(Matches paramMatches) {
		visitBinaryExpression(paramMatches);

	}

	@Override
	public void visit(BitwiseAnd paramBitwiseAnd) {
		visitBinaryExpression(paramBitwiseAnd);
	}

	@Override
	public void visit(BitwiseOr paramBitwiseOr) {
		visitBinaryExpression(paramBitwiseOr);
	}

	@Override
	public void visit(BitwiseXor paramBitwiseXor) {
		visitBinaryExpression(paramBitwiseXor);
	}

	public void visitBinaryExpression(BinaryExpression binaryExpression) {
		binaryExpression.getLeftExpression().accept(this);
		binaryExpression.getRightExpression().accept(this);
	}

	@Override
	public void visit(CastExpression paramCastExpression) {
	}

	@Override
	public void visit(Modulo paramModulo) {
	}

	@Override
	public void visit(AnalyticExpression paramAnalyticExpression) {
	}

	@Override
	public void visit(WithinGroupExpression paramWithinGroupExpression) {
	}

	@Override
	public void visit(ExtractExpression paramExtractExpression) {
	}

	@Override
	public void visit(IntervalExpression paramIntervalExpression) {
		// paramIntervalExpression.getExpression().accept(this);
	}

	@Override
	public void visit(OracleHierarchicalExpression paramOracleHierarchicalExpression) {
	}

	@Override
	public void visit(RegExpMatchOperator paramRegExpMatchOperator) {
	}

	@Override
	public void visit(JsonExpression paramJsonExpression) {
	}

	@Override
	public void visit(RegExpMySQLOperator paramRegExpMySQLOperator) {
	}

	@Override
	public void visit(UserVariable paramUserVariable) {
	}

	@Override
	public void visit(NumericBind paramNumericBind) {
	}

	@Override
	public void visit(KeepExpression paramKeepExpression) {
	}

	@Override
	public void visit(MySQLGroupConcat paramMySQLGroupConcat) {
	}

	@Override
	public void visit(RowConstructor paramRowConstructor) {
	}

	@Override
	public void visit(Table paramTable) {
		String table = paramTable.getName();
		if (null != tn) {
			paramTable.setName(tn);
		}
		originalTable = table;
	}

	@Override
	public void visit(SubSelect paramSubSelect) {
		paramSubSelect.getSelectBody().accept(this);
	}

	@Override
	public void visit(SubJoin paramSubJoin) {
		paramSubJoin.getLeft().accept(this);
		paramSubJoin.getJoin().getRightItem().accept(this);
	}

	@Override
	public void visit(LateralSubSelect paramLateralSubSelect) {
	}

	@Override
	public void visit(ValuesList paramValuesList) {
	}

	@Override
	public void visit(PlainSelect plainSelect) {
		plainSelect.getFromItem().accept(this);
		if (plainSelect.getJoins() != null) {
			Iterator<Join> joinsIt = plainSelect.getJoins().iterator();
			while (joinsIt.hasNext()) {
				// Join join = (Join) joinsIt.next();
				Join join = joinsIt.next();
				join.getRightItem().accept(this);
			}
		}
		if (null != plainSelect.getWhere()) {
			plainSelect.getWhere().accept(this);
		}
		if (null != plainSelect.getOrderByElements()) {
			for (OrderByElement obe : plainSelect.getOrderByElements()) {
				orderBys.add(new SqlConverterOrderBy(obe.getExpression().toString(), obe.isAsc()));
			}
		}
	}

	@Override
	public void visit(SetOperationList paramSetOperationList) {
	}

	@Override
	public void visit(WithItem paramWithItem) {
	}

}
